#region Copyright (c) 2006-2009 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2009 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Globalization;
using System.Diagnostics;

using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;

using IServiceProvider = System.IServiceProvider;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
using VSStd97CmdID = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;

using Widgetsphere.Generator.Common.Forms;
using Widgetsphere.Generator.Common.Util;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.VsCodeTools;
using EnvDTE;
using System.Collections;

namespace Widgetsphere.VsCodeTools
{
	public sealed partial class ModelTreeEditor : ModelTree,
																													IVsPersistDocData,
																													IPersistFileFormat,
																													IOleCommandTarget,
		//														IVsFindTarget,
																													IVsWindowPane,
																													IVsDocDataFileChangeControl,
																													IVsFileChangeEvents
	{
		#region Class Members

		private const uint _myFormat = 0;
		private const string _myExtension = ".wsgen";

		private int FilesSkipped = 0;
		private int FilesSuccess = 0;
		private int FilesFailed = 0;
		private List<ProjectItemGeneratedEventArgs> GeneratedFileList = new List<ProjectItemGeneratedEventArgs>();

		#endregion

		#region class variables
		private ServiceProvider _vsServiceProvider = null;
		private Widgetsphere.VsCodeTools.VsCodeToolsPackage _myPackage = null;
		private IVsRunningDocumentTable _runningDocTable;

		// Flag true when we are loading the file. It is used to avoid to change the isDirty flag
		// when the changes are related to the load operation.
		private bool _loading;
		// This flag is true when we are asking the QueryEditQuerySave service if we can edit the
		// file. It is used to avoid to have more than one request queued.
		private bool _gettingCheckoutStatus;

		// Counter of the file system changes to ignore.
		private int _changesToIgnore;
		// Cookie for the subscription to the file system notification events.
		private uint _fileChangeNotifyCookie;
		// This flag is used to know when the reload procedure is running.
		private bool _isFileReloading;


		//private System.ComponentModel.Container _components = null;
		private System.Windows.Forms.Timer _reloadTimer;

		#endregion

		#region Class Constructors

		public ModelTreeEditor()
		{
			try
			{
				PrivateInit(null);
				InitializeComponent();
				lvwOutput.Columns.Add("Description", 500);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		public ModelTreeEditor(Widgetsphere.VsCodeTools.VsCodeToolsPackage package)
		{
			try
			{
				PrivateInit(package);
				InitializeComponent();
				lvwOutput.Columns.Add("Description", 500);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		#endregion

		#region IVsPersistDocData Members

		public int Close()
		{
			try
			{
				this.Dispose();
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		public int GetGuidEditorType(out Guid pClassID)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).GetClassID(out pClassID);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pClassID = new Guid();
			}
			return retVal;
		}

		public int IsDocDataDirty(out int pfDirty)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).IsDirty(out pfDirty);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pfDirty = int.MinValue;
			}
			return retVal;
		}

		public int IsDocDataReloadable(out int pfReloadable)
		{
			try
			{
				pfReloadable = 1;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pfReloadable = 1;
			}
			return VSConstants.S_OK;
		}

		public int LoadDocData(string pszMkDocument)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(pszMkDocument, 0, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return retVal;
		}

		public int OnRegisterDocData(uint docCookie, IVsHierarchy pHierNew, uint itemidNew)
		{
			return VSConstants.S_OK;
		}

		public int ReloadDocData(uint grfFlags)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).Load(null, grfFlags, 0);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return retVal;
		}

		public int RenameDocData(uint grfAttribs, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
		{
			return VSConstants.S_OK;
		}

		private bool SaveDoc()
		{
			IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetVsService(typeof(SVsQueryEditQuerySave));

			// Call QueryEditQuerySave
			uint result = 0;
			int hr = queryEditQuerySave.QuerySaveFile(this.FileName, 0, null, out result);
			if (ErrorHandler.Failed(hr))
				return false;

			// Call the shell to do the save for us
			IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
			string pbstrMkDocumentNew = null;
			int pfSaveCanceled = 0;
			hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, (IPersistFileFormat)this, this.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
			if (ErrorHandler.Failed(hr))
				return false;

			return true;

		}

		public int SaveDocData(VSSAVEFLAGS dwSave, out string pbstrMkDocumentNew, out int pfSaveCanceled)
		{
			int hr = VSConstants.S_OK;
			try
			{
				pbstrMkDocumentNew = null;
				pfSaveCanceled = 0;

				switch (dwSave)
				{
					case VSSAVEFLAGS.VSSAVE_Save:
					case VSSAVEFLAGS.VSSAVE_SilentSave:
						{
							IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)GetVsService(typeof(SVsQueryEditQuerySave));

							// Call QueryEditQuerySave
							uint result = 0;
							hr = queryEditQuerySave.QuerySaveFile(this.FileName, 0, null, out result);
							if (ErrorHandler.Failed(hr))
								return hr;

							// Process according to result from QuerySave
							switch ((tagVSQuerySaveResult)result)
							{
								case tagVSQuerySaveResult.QSR_NoSave_Cancel:
									// Note that this is also case tagVSQuerySaveResult.QSR_NoSave_UserCanceled because these
									// two tags have the same value.
									pfSaveCanceled = ~0;
									break;

								case tagVSQuerySaveResult.QSR_SaveOK:
									{
										// Call the shell to do the save for us
										IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
										hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, this.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
										if (ErrorHandler.Failed(hr))
											return hr;
									}
									break;

								case tagVSQuerySaveResult.QSR_ForceSaveAs:
									{
										// Call the shell to do the SaveAS for us
										IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
										hr = uiShell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SaveAs, (IPersistFileFormat)this, this.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
										if (ErrorHandler.Failed(hr))
											return hr;
									}
									break;

								case tagVSQuerySaveResult.QSR_NoSave_Continue:
									// In this case there is nothing to do.
									break;

								default:
									throw new COMException(Widgetsphere.VsCodeTools.ModelEditor.SCCError);
							}
							break;
						}
					case VSSAVEFLAGS.VSSAVE_SaveAs:
					case VSSAVEFLAGS.VSSAVE_SaveCopyAs:
						{
							// Make sure the file name as the right extension
							if (string.Compare(_myExtension, System.IO.Path.GetExtension(this.FileName), true, CultureInfo.InvariantCulture) != 0)
							{
								this.FileName += _myExtension;
							}
							// Call the shell to do the save for us
							IVsUIShell uiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
							hr = uiShell.SaveDocDataToFile(dwSave, (IPersistFileFormat)this, this.FileName, out pbstrMkDocumentNew, out pfSaveCanceled);
							if (ErrorHandler.Failed(hr))
								return hr;
							break;
						}
					default:
						throw new ArgumentException(Widgetsphere.VsCodeTools.ModelEditor.BadSaveFlags);
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pbstrMkDocumentNew = string.Empty;
				pfSaveCanceled = 0;
			}
			return hr;

		}

		public int SetUntitledDocPath(string pszDocDataPath)
		{
			int retVal = int.MinValue;
			try
			{
				retVal = ((IPersistFileFormat)this).InitNew(_myFormat);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return retVal;
		}

		#endregion

		#region IPersistFileFormat Members

		public int GetClassID(out Guid pClassID)
		{
			try
			{
				((Microsoft.VisualStudio.OLE.Interop.IPersist)this).GetClassID(out pClassID);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pClassID = new Guid();
			}
			return VSConstants.S_OK;
		}

		public int GetCurFile(out string ppszFilename, out uint pnFormatIndex)
		{
			try
			{
				pnFormatIndex = _myFormat;
				ppszFilename = this.FileName;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pnFormatIndex = 0;
				ppszFilename = string.Empty;
			}
			return VSConstants.S_OK;
		}

		public int GetFormatList(out string ppszFormatList)
		{
			try
			{
				ppszFormatList = Widgetsphere.VsCodeTools.ModelEditor.EditorFormatString;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				ppszFormatList = string.Empty;
			}
			return VSConstants.S_OK;
		}

		public int InitNew(uint nFormatIndex)
		{

			try
			{
				if (nFormatIndex != _myFormat)
				{
					throw new ArgumentException(Widgetsphere.VsCodeTools.ModelEditor.UnknownFileFormat);
				}
				// until someone change the file, we can consider it not dirty as
				// the user would be annoyed if we prompt him to save an empty file
				this._isDirty = false;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			return VSConstants.S_OK;
		}

		public int IsDirty(out int pfIsDirty)
		{
			try
			{
				if (this._isDirty)
				{
					pfIsDirty = 1;
				}
				else
				{
					pfIsDirty = 0;
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				pfIsDirty = 0;
			}
			return VSConstants.S_OK;
		}

		public new int Load(string pszFilename, uint grfMode, int fReadOnly)
		{
			if ((pszFilename == null) && ((this.FileName == null) || (this.FileName.Length == 0)))
			{
				throw new ArgumentNullException(Widgetsphere.VsCodeTools.ModelEditor.NullFileName);
			}

			_loading = true;
			int hr = VSConstants.S_OK;
			try
			{
				bool isReload = false;

				// If the new file name is null, then this operation is a reload
				if (pszFilename == null)
				{
					isReload = true;
				}

				// Show the wait cursor while loading the file
				IVsUIShell vsUiShell = (IVsUIShell)GetVsService(typeof(SVsUIShell));
				if (vsUiShell != null)
				{
					// Note: we don't want to throw or exit if this call fails, so
					// don't check the return code.
					vsUiShell.SetWaitCursor();
				}

				// Set the new file name
				if (!isReload)
				{
					// Unsubscribe from the notification of the changes in the previous file.
					AdviseFileChange(false);
					this.FileName = pszFilename;
				}
				// Load the file

				this.LoadFile(this.FileName);
				this._isDirty = false;

				// Subscribe for the notification on file changes.
				if (!isReload)
				{
					AdviseFileChange(true);
				}

				// Notify the load or reload
				NotifyDocChanged();
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			finally
			{
				_loading = false;
			}
			return hr;

		}

		public int Save(string pszFilename, int fRemember, uint nFormatIndex)
		{
			try
			{
				// We don't want to be notify for this change of the file.
				AdviseFileChange(false);

				// If file is null or same --> SAVE
				if (pszFilename == null || pszFilename == this.FileName)
				{
					this.SaveModel();
					this._isDirty = false;
				}
				// If remember --> SaveAs
				else if (fRemember != 0)
				{
					this.FileName = pszFilename;
					this.SaveModel();
					this._isDirty = false;
				}
				// Else, Save a Copy As
				else
				{
					this.SaveFile(pszFilename);
				}
				// Now that the file is saved (and maybe renamed) we can subscribe again
				// for the file system events.
				AdviseFileChange(true);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			return VSConstants.S_OK;

		}

		public int SaveCompleted(string pszFilename)
		{
			return VSConstants.S_OK;
		}

		#endregion

		#region IOleCommandTarget Members
		public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
		{
			if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
			{
				// Process standard Visual Studio Commands
				switch (nCmdID)
				{
					case (uint)VSStd97CmdID.Exit:
					default: return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else if (pguidCmdGroup == GuidList.guidVsCodeToolsCmdSet)
			{
				switch (nCmdID)
				{
					// if we had commands specific to our editor, they would be processed here
					case 0x100:
						this.SaveDoc();
						this.GenerateAll();
						break;
					case 0x103:
						this.ImportDb();
						break;
					case 0x104:
						//ISelectionContainer propertyWindow = GetPropertiesWindow();
						//propertyWindow.SelectObjects(1, new object[] { this }, 0);
						this.Verify();
						break;
					default:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else
				return (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP;

			return VSConstants.S_OK;

		}

		public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
		{
			if ((prgCmds == null))
				return VSConstants.E_INVALIDARG;


			if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97)
			{
				// Process standard Commands
				switch (prgCmds[0].cmdID)
				{
					case (uint)VSStd97CmdID.Exit:
					default: return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else if (pguidCmdGroup == GuidList.guidVsCodeToolsCmdSet)
			{
				// Process our Commands
				switch (prgCmds[0].cmdID)
				{
					case 0x100:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.MSOCMDF_ENABLED);
					case 0x102:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.MSOCMDF_ENABLED);
					default:
						return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);
				}
			}
			else
				return (int)(Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED);

		}

		#endregion

		#region IVsWindowPane Members

		public int ClosePane()
		{
			try
			{
				this.Dispose(true);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		public int CreatePaneWindow(IntPtr hwndParent, int x, int y, int cx, int cy, out IntPtr hwnd)
		{
			try
			{
				Win32Methods.SetParent(Handle, hwndParent);
				hwnd = Handle;

				Size = new System.Drawing.Size(cx - x, cy - y);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
				hwnd = new IntPtr();
			}
			return VSConstants.S_OK;
		}

		public int GetDefaultSize(SIZE[] pSize)
		{
			try
			{
				if (pSize.Length >= 1)
				{
					pSize[0].cx = Size.Width;
					pSize[0].cy = Size.Height;
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			return VSConstants.S_OK;
		}

		public int LoadViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SaveViewState(IStream pStream)
		{
			return VSConstants.S_OK;
		}

		public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)
		{
			try
			{
				_vsServiceProvider = new ServiceProvider(psp);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return VSConstants.S_OK;
		}

		public int TranslateAccelerator(MSG[] lpmsg)
		{
			return VSConstants.S_FALSE;
		}

		#endregion

		#region IVsFindTarget Members
		/*
		public int Find(string pszSearch, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out uint pResult)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetCapabilities(bool[] pfImage, uint[] pgrfOptions)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetCurrentSpan(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetFindState(out object ppunk)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetMatchRect(RECT[] prc)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetProperty(uint propid, out object pvar)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int GetSearchImage(uint grfOptions, IVsTextSpanSet[] ppSpans, out IVsTextImage ppTextImage)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int MarkSpan(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int NavigateTo(TextSpan[] pts)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int NotifyFindTarget(uint notification)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int Replace(string pszSearch, string pszReplace, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out int pfReplaced)
		{
			throw new Exception("The method or operation is not implemented.");
		}

		public int SetFindState(object pUnk)
		{
			throw new Exception("The method or operation is not implemented.");
		}
		*/
		#endregion

		#region IVsDocDataFileChangeControl Members

		public int IgnoreFileChanges(int fIgnore)
		{
			try
			{
				if (0 != fIgnore)
				{
					++_changesToIgnore;
				}
				else
				{
					if (_changesToIgnore > 0)
					{
						--_changesToIgnore;
					}
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			return VSConstants.S_OK;
		}

		#endregion

		#region IVsFileChangeEvents Members

		public int DirectoryChanged(string pszDirectory)
		{
			return VSConstants.S_OK;
		}

		public int FilesChanged(uint cChanges, string[] rgpszFile, uint[] rggrfChange)
		{
			try
			{			// Check the number of changes.
				if (0 == cChanges)
				{
					// Why this event was called if there is no change???
					return VSConstants.E_UNEXPECTED;
				}

				// If the counter of the changes to ignore (set by IVsDocDataFileChangeControl.IgnoreFileChanges)
				// is zero we can process this set of changes, otherwise ignore it.
				if (0 != _changesToIgnore)
					return VSConstants.S_OK;

				// Now scan the list of the changed files to find if the one opened in the editor
				// is one of the changed
				for (int i = 0; i < cChanges; ++i)
				{
					if (string.Compare(this.FileName, rgpszFile[i], true, CultureInfo.InvariantCulture) == 0)
					{
						// The file opened in the editor is changed.
						// The first step now is to find the kind of change.
						uint contentChange = (uint)_VSFILECHANGEFLAGS.VSFILECHG_Size | (uint)_VSFILECHANGEFLAGS.VSFILECHG_Time;
						if ((rggrfChange[i] & contentChange) != 0)
						{
							// The content of the file is changed outside the editor, so we need to
							// prompt the user for re-load the file. It is possible to have multiple
							// notification for this change, but we want to prompt the user and reload
							// the file only once
							if (_reloadTimer.Enabled || _isFileReloading)
							{
								// The reload is running, so we don't need to start it again.
								return VSConstants.S_OK;
							}

							// Set the timer timeout and start it
							_reloadTimer.Interval = 500;
							_reloadTimer.Enabled = true;
							_reloadTimer.Start();
						}
					}
				}
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

			return VSConstants.S_OK;
		}
		#endregion

		#region helper methods
		private void PrivateInit(Widgetsphere.VsCodeTools.VsCodeToolsPackage package)
		{
			try
			{
				_myPackage = package;
				_loading = false;
				_gettingCheckoutStatus = false;
				_isFileReloading = false;
				_fileChangeNotifyCookie = VSConstants.VSCOOKIE_NIL;
				_changesToIgnore = 0;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		private object GetVsService(Type serviceType)
		{
			return _vsServiceProvider.GetService(serviceType);
		}

		private void AdviseFileChange(bool subscribe)
		{
			try
			{			// Get the VsFileChangeEx service; this service will call us back when a file system
				// event will occur on the file(s) that we register.
				IVsFileChangeEx fileChangeService = (IVsFileChangeEx)GetVsService(typeof(SVsFileChangeEx));
				int hr = VSConstants.S_OK;

				if (!subscribe)
				{
					// If the goal is to unsubscribe, but there is no subscription active, exit.
					if (_fileChangeNotifyCookie == VSConstants.VSCOOKIE_NIL)
						return;

					// Now there is an active subscription, so unsubscribe.
					hr = fileChangeService.UnadviseFileChange(_fileChangeNotifyCookie);
					// No more subscription active, so set the cookie to NIL
					_fileChangeNotifyCookie = VSConstants.VSCOOKIE_NIL;
					ErrorHandler.ThrowOnFailure(hr);
					return;
				}

				// Here we want to subscribe for notification when the file opened in the editor changes.
				uint eventsToSubscribe = (uint)_VSFILECHANGEFLAGS.VSFILECHG_Size |
																												 (uint)_VSFILECHANGEFLAGS.VSFILECHG_Time;
				hr = fileChangeService.AdviseFileChange(
								this.FileName,                        // The file to check
								eventsToSubscribe,                // Filter to use for the notification
								(IVsFileChangeEvents)this,        // The callback to call
								out _fileChangeNotifyCookie);    // Cookie used to identify this subscription.
				ErrorHandler.ThrowOnFailure(hr);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		private void NotifyDocChanged()
		{
			try
			{// Get a reference to the Running Document Table
				if (null == _runningDocTable)
				{
					_runningDocTable = (IVsRunningDocumentTable)GetVsService(typeof(SVsRunningDocumentTable));
				}

				// Lock the document
				uint docCookie;
				IVsHierarchy hierarchy;
				uint itemID;
				IntPtr docData;
				int hr = _runningDocTable.FindAndLockDocument(
								(uint)_VSRDTFLAGS.RDT_ReadLock,
								this.FileName,
								out hierarchy,
								out itemID,
								out docData,
								out docCookie
				);
				ErrorHandler.ThrowOnFailure(hr);

				// Send the notification. Note that we do not throw now in case of error because before
				// we have to unlock the document.
				hr = _runningDocTable.NotifyDocumentChanged(docCookie, (uint)__VSRDTATTRIB.RDTA_DocDataReloaded);

				// Unlock the document.
				// Note that we have to unlock the document even if the previous call failed.
				_runningDocTable.UnlockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, docCookie);

				// Check if the call to NotifyDocChanged failed.
				ErrorHandler.ThrowOnFailure(hr);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		private void _reloadTimer_Tick(object sender, System.EventArgs e)
		{
			try
			{			// Here we want to check if we can process the reload.
				// We don't want to show the reload popup when the shell is not the active window,
				// so as first we check if the shell has the focus.
				// To do so we use the Win32 function GetActiveWindow that will return the handle
				// of the active window in the current application and NULL (0) if no window has
				// the focus.
				if (0 != NativeMethods.GetActiveWindow())
				{
					try
					{
						// Set the flag about the reload status. 
						// It is important to avoid that the popup will show multiple times for the same file.
						_isFileReloading = true;

						// The timer was used only to not run this procedure in syncronously inside the
						// notification function, so now we can stop end disable it.
						_reloadTimer.Stop();
						_reloadTimer.Enabled = false;

						// Build the title and message for the popup.
						string message = this.FileName + "\n\n" + Widgetsphere.VsCodeTools.ModelEditor.OutsideEditorFileChange;
						string title = "Microsoft Visual Studio";
						// Show the popup
						System.Windows.Forms.DialogResult res = MessageBox.Show(
										this,
										message,
										title,
										System.Windows.Forms.MessageBoxButtons.YesNo,
										System.Windows.Forms.MessageBoxIcon.Question);
						if (res == System.Windows.Forms.DialogResult.Yes)
						{
							// The user wants to reload the data, so let's call the function that will do the job.
							int hr = ((IVsPersistDocData)this).ReloadDocData(0);
						}
					}
					finally
					{
						// Reset the flag about the reload status.
						_isFileReloading = false;
					}
				}
			}
			catch (ObjectDisposedException ex)
			{
				//This was throwing an error when saving the MOD file.
				//I have just disabled it since there is nothing that I can do
				//throw ex;
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}

		}

		internal static class NativeMethods
		{
			// We need this Win32 function to find out if the shell has the focus.
			[DllImport("user32.Dll")]
			static public extern int GetActiveWindow();
		}
		#endregion

		#region Generate Methods

		// private IVsOutputWindowPane outputWindow = null;
		private void GenerateAll()
		{
			try
			{
				DateTime startTime = DateTime.Now;
				//object outputWindow = this.CreatePane(new Guid("{2C997982-CA6C-4640-8073-DB1B9BD8D93B}"), "Generation", true, true);

				this.FilesSkipped = 0;
				this.FilesSuccess = 0;
				this.FilesFailed = 0;
				this.GeneratedFileList.Clear();

				this.tvwModel.Enabled = false;
				this.ResetContentPane();

				//If error then do not gen
				MessageCollection messageCollection = this.Generator.RootController.Verify();
				if (messageCollection.Count > 0)
				{
					this.lvwError.ClearMessages();
					this.lvwError.AddMessages(messageCollection);
					MessageBox.Show("The model cannot be generated until all errors are corrected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return;
				}

				tabControl1.SelectedTab = tabControl1.TabPages[1]; //Show the output window
				lvwOutput.Items.Clear();
				GeneratorHelper g = new GeneratorHelper();

				List<Type> excludeList = new List<Type>();
				List<Type> generatorTypeList = g.GetProjectGenerators(this.Generator);
				using (GenerateSettings F = new GenerateSettings(this.Generator, generatorTypeList))
				{
					if (F.ShowDialog() != DialogResult.OK) return;
					excludeList = F.ExcludeList;
				}

				g.ProjectItemGenerated += new ProjectItemGeneratedEventHandler(ProjectItemGeneratedHandler);
				g.GenerationComplete += new ProjectItemGenerationCompleteEventHandler(GenerationCompleteHandler);
				g.ProjectItemGeneratedError += new ProjectItemGeneratedEventHandler(ProjectItemGeneratedError);
				g.GenerateAll(this.Generator, excludeList);

				DateTime endTime = DateTime.Now;
				TimeSpan duration = endTime.Subtract(startTime);
				using (StatisticsForm F = new StatisticsForm())
				{
					string text = "The generation was successful.\r\n\r\n";
					text += "Files generated: " + this.FilesSuccess + "\r\n";
					text += "Files skipped: " + this.FilesSkipped + "\r\n";
					text += "Files failed: " + this.FilesFailed + "\r\n";
					text += "\r\n\r\n";
					text += "Generation time: " + duration.Hours.ToString("00") + ":" +
						duration.Minutes.ToString("00") + ":" +
						duration.Seconds.ToString("00");
					F.DisplayText = text;
					F.GeneratedFileList = this.GeneratedFileList;
					F.ShowDialog();
				}
			
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			finally
			{
				this.tvwModel.Enabled = true;
			}
		}

		private void ProjectItemGeneratedError(object sender, ProjectItemGeneratedEventArgs e)
		{
			try
			{
				this.lvwError.AddMessage(new Widgetsphere.Generator.Common.GeneratorFramework.Message(MessageTypeConstants.Error, "Error Generating: ProjectName: " + e.ProjectName + "  ParentItemName: " + e.ParentItemName + "  ProjectItemName: " + e.ProjectItemName, null));
				lvwError.EnsureVisible(lvwError.Items.Count - 1);
				Application.DoEvents();
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		private void GenerationCompleteHandler(object sender, ProjectItemGenerationCompleteEventArgs e)
		{
			//lvwOutput.Items.Add("Generation Complete");
			//lvwOutput.EnsureVisible(lvwOutput.Items.Count - 1);
			//Application.DoEvents();
		}

		private void ProjectItemGeneratedHandler(object sender, ProjectItemGeneratedEventArgs e)
		{
			try
			{
				ListViewItem newItem = lvwOutput.Items.Add("File " + e.FileState.ToString() + ": " + e.ProjectItemName);
				if (e.FileState == EnvDTEHelper.FileStateConstants.Failed)
					newItem.ForeColor = Color.Red;

				if (e.FileState == EnvDTEHelper.FileStateConstants.Skipped)
					this.FilesSkipped++;
				if (e.FileState == EnvDTEHelper.FileStateConstants.Success)
					this.FilesSuccess++;
				if (e.FileState == EnvDTEHelper.FileStateConstants.Failed)
					this.FilesFailed++;

				this.GeneratedFileList.Add(e);

				lvwOutput.EnsureVisible(lvwOutput.Items.Count - 1);
				Application.DoEvents();
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
		}

		private ISelectionContainer GetPropertiesWindow()
		{			
			try
			{				
				foreach (Window w in EnvDTEHelper.Instance.ApplicationObject.Windows)
				{
					if (w.Caption == "Properties")
					{
						return (ISelectionContainer)w;						
					}
				}				
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			} 
			return null;		
		}

		private IVsOutputWindowPane CreatePane(Guid paneGuid, string title, bool visible, bool clearWithSolution)
		{			
			IVsOutputWindowPane pane = null;
			try
			{
				IVsOutputWindow output = null;				
				foreach (Window w in EnvDTEHelper.Instance.ApplicationObject.Windows)
				{					
					if (w.Caption == "Output")
					{
						output = (IVsOutputWindow)w.Object;
						break;
					}
				}

				// Create a new pane.
				output.CreatePane(
								ref paneGuid,
								title,
								Convert.ToInt32(visible),
								Convert.ToInt32(clearWithSolution));

				// Retrieve the new pane.
				output.GetPane(ref paneGuid, out pane);
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			return pane;
		}

		private void ImportDb()
		{
			try
			{
				this.Enabled = false;
				Generator.HandleCommand("ImportModel");
			}
			catch (Exception ex)
			{
				GlobalHelper.SetErr(ex);
			}
			finally
			{
				this.Enabled = true;
			}
		}

		#endregion

	}
}